package com.xsp.mybatisxmlsqlparser.parser;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.xsp.mybatisxmlsqlparser.node.ElementCounters;
import com.xsp.mybatisxmlsqlparser.node.ParseNodeTypeEnum;
import com.xsp.mybatisxmlsqlparser.node.ReplacedElement;
import com.xsp.mybatisxmlsqlparser.replacer.CustomPronouns;
import com.xsp.mybatisxmlsqlparser.replacer.SpecialMarkReplacer;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.*;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class CustomParserService implements ParserService {

    /**
     * 解析DOM4J节点，并转换为JSqlParser可以解析出来的SQL语句字符串
     *
     * @param node              DOM4J节点
     * @param counters          计数器
     * @param newClearStatement 用于存储解析后的SQL语句
     */
    public void parseNodeForJSqlParserString(Node node,
                                             ElementCounters counters,
                                             ReplacedElement replacedElement,
                                             StringBuilder newClearStatement) {
        // 判断节点类型是否为元素
        switch (node.getNodeType()) {
            case Node.ELEMENT_NODE:
                Element element = (Element) node;
                switch (element.getName().toUpperCase()) {
                    case "WHERE":
                        newClearStatement.append(CustomPronouns.whereTagPlaceholder());
                        replacedElement.add(ParseNodeTypeEnum.WHERE.name(), element);
                        break;
                    case "IF":
                        newClearStatement.append(CustomPronouns.ifStartTagPlaceholder(counters.get(ParseNodeTypeEnum.IF.name())));
                        replacedElement.add(ParseNodeTypeEnum.IF.name(), element);
                        break;
                    case "CHOOSE":
                        newClearStatement.append(CustomPronouns.chooseStartTagPlaceholder(counters.get(ParseNodeTypeEnum.CHOOSE.name())));
                        replacedElement.add(ParseNodeTypeEnum.CHOOSE.name(), element);
                        break;
                    case "WHEN":
                        newClearStatement.append(CustomPronouns.whenStartTagPlaceholder(counters.get(ParseNodeTypeEnum.WHEN.name())));
                        replacedElement.add(ParseNodeTypeEnum.WHEN.name(), element);
                        break;
                    case "OTHERWISE":
                        newClearStatement.append(CustomPronouns.otherwiseStartTagPlaceholder(counters.get(ParseNodeTypeEnum.OTHERWISE.name())));
                        replacedElement.add(ParseNodeTypeEnum.OTHERWISE.name(), element);
                        break;
                    case "FOREACH":
                        newClearStatement.append(CustomPronouns.forEachPlaceholder(counters.get(ParseNodeTypeEnum.FOREACH.name())));
                        replacedElement.add(ParseNodeTypeEnum.FOREACH.name(), element);
                        break;
                    default:
                        break;
                }
                // 获取元素的子元素列表，递归遍历
                List<Node> childElements = element.content();
                for (Node child : childElements) {
                    parseNodeForJSqlParserString(child, counters, replacedElement, newClearStatement);
                }

                // 添加结束标签
                switch (element.getName().toUpperCase()) {
                    case "WHERE":
                        newClearStatement.append(CustomPronouns.whereEndTagPlaceholder());
                        counters.add(ParseNodeTypeEnum.WHERE.name());
                        break;
                    case "IF":
                        newClearStatement.append(CustomPronouns.ifEndTagPlaceholder(counters.get(ParseNodeTypeEnum.IF.name())));
                        counters.add(ParseNodeTypeEnum.IF.name());
                        break;
                    case "CHOOSE":
                        newClearStatement.append(CustomPronouns.chooseEndTagPlaceholder(counters.get(ParseNodeTypeEnum.CHOOSE.name())));
                        counters.add(ParseNodeTypeEnum.CHOOSE.name());
                        break;
                    case "WHEN":
                        newClearStatement.append(CustomPronouns.whenEndTagPlaceholder(counters.get(ParseNodeTypeEnum.WHEN.name())));
                        counters.add(ParseNodeTypeEnum.WHEN.name());
                        break;
                    case "OTHERWISE":
                        newClearStatement.append(CustomPronouns.otherwiseEndTagPlaceholder(counters.get(ParseNodeTypeEnum.OTHERWISE.name())));
                        counters.add(ParseNodeTypeEnum.OTHERWISE.name());
                        break;
                    case "FOREACH":
                        newClearStatement.append(CustomPronouns.forEachPlaceholder(counters.get(ParseNodeTypeEnum.FOREACH.name())).trim());
                        counters.add(ParseNodeTypeEnum.FOREACH.name());
                        break;
                    default:
                        break;
                }
                break;
            case Node.TEXT_NODE:
                Text textNode = (Text) node;
                String text = textNode.getText();
                Element parentElement = textNode.getParent();

                if (StrUtil.isNotBlank(text)) {
                    if (parentElement.getName().equalsIgnoreCase("WHERE")) {
                        //为WHERE下 没有AND的情况添加AND
                        if (!StrUtil.startWithAnyIgnoreCase(text.trim(), "AND", "OR", "NOT")) {
                            newClearStatement.append(" AND ");
                        }
                    }

                    if (parentElement.getName().equalsIgnoreCase("FOREACH")) {
                        String clearText = text.trim().replace("\n", "");
                        newClearStatement.append(clearText);
                    } else {
                        newClearStatement.append(text);
                    }
                }
                break;
            case Node.COMMENT_NODE:
                Comment commentNode = (Comment) node;
                // 添加注释
                newClearStatement.append("<!--").append(commentNode.getText()).append("-->");
                break;
            default:
                break;
        }
    }

    public String getElementCounters(Element element) {
        StringBuilder tag = new StringBuilder();
        tag.append('<').append(element.getName());
        if (CollectionUtil.isNotEmpty(element.attributes())) {
            for (Attribute attr : element.attributes()) {
                tag.append(' ').append(attr.asXML());
            }
        }
        tag.append('>');
        return tag.toString();
    }

    @Override
    public String parseJSqlParserStringForDom4j(String sql, ElementCounters counters, ReplacedElement replacedElement) {
        Map<String, List<Element>> replacedElementMap = replacedElement.map();
        for (Map.Entry<String, List<Element>> entry : replacedElementMap.entrySet()) {
            for (int i = 1; i <= entry.getValue().size(); i++) {
                Element element = entry.getValue().get(i - 1);
                switch (entry.getKey()) {
                    case "WHERE":
                        String whereTag = getElementCounters(element);
                        sql = sql.replace(CustomPronouns.whereTagPlaceholder().trim(), whereTag);
                        sql = sql.replace(CustomPronouns.whereEndTagPlaceholder().trim(), "</" + element.getName() + ">");
                        break;
                    case "IF":
                        String ifTag = getElementCounters(element);
                        sql = sql.replace(CustomPronouns.ifStartTagPlaceholder(i).trim(), ifTag);
                        sql = sql.replace(CustomPronouns.ifEndTagPlaceholder(i).trim(), "</" + element.getName() + ">");
                        break;
                    case "CHOOSE":
                        String chooseTag = getElementCounters(element);
                        sql = sql.replace(CustomPronouns.chooseStartTagPlaceholder(i).trim(), chooseTag);
                        sql = sql.replace(CustomPronouns.chooseEndTagPlaceholder(i).trim(), "</" + element.getName() + ">");
                        break;
                    case "WHEN":
                        String whenTag = getElementCounters(element);
                        sql = sql.replace(CustomPronouns.whenStartTagPlaceholder(i).trim(), whenTag);
                        sql = sql.replace(CustomPronouns.whenEndTagPlaceholder(i).trim(), "</" + element.getName() + ">");
                        break;
                    case "OTHERWISE":
                        String otherwiseTag = getElementCounters(element);
                        sql = sql.replace(CustomPronouns.otherwiseStartTagPlaceholder(i).trim(), otherwiseTag);
                        sql = sql.replace(CustomPronouns.otherwiseEndTagPlaceholder(i).trim(), "</" + element.getName() + ">");
                        break;
                    case "FOREACH":
                        //所有前后包含的标签
                        String forEachTag = getElementCounters(element);
                        sql = sql.replace(CustomPronouns.forEachPlaceholder(i).trim(), "\n" + element.asXML());
                        break;
                    default:
                        break;
                }
            }
        }
        return sql;
    }

    /**
     * 替换特殊标记为可解析字符
     *
     * @param sqlText 带有特殊标记的sql文本
     * @return 替换后的sql文本
     */
    @Override
    public String ReplaceSpecialMark(String sqlText) {
        sqlText = SpecialMarkReplacer.replacePattern(sqlText, SpecialMarkReplacer.DOLLARREGEXA, SpecialMarkReplacer.DOLLARSTRINGP);

        sqlText = SpecialMarkReplacer.replacePattern(sqlText, SpecialMarkReplacer.WELLMARKREGEXA, SpecialMarkReplacer.WELLMARKSTRINGP);

        return sqlText;
    }

    /**
     * 可解析字符替换回特殊标记
     * @param sqlText 带有特殊标记的sql文本
     * @return 替换后的sql文本
     */
    @Override
    public String ReplacePlaceholder(String sqlText) {
        sqlText = SpecialMarkReplacer.replacePattern(sqlText, SpecialMarkReplacer.WELLMARKSTRINGA, SpecialMarkReplacer.WELLMARKREGEXP);

        sqlText = SpecialMarkReplacer.replacePattern(sqlText, SpecialMarkReplacer.DOLLARSTRINGA, SpecialMarkReplacer.DOLLARREGEXA);

        sqlText = SpecialMarkReplacer.replaceOrRestoreSpecialCharacters(sqlText, false);
        return sqlText;
    }
}
